import datetime
import os
import re
import sys
import time

from modules.base import Base


class BaseUpgrade(Base):
    def __init__(self, **kwargs):
        super(BaseUpgrade, self).__init__()
        self.ipaddress = kwargs.get('ipaddress', '')
        self.ipaddress_std1 = kwargs.get('ipaddress_std1', '')
        self.ipaddress_std2 = kwargs.get('ipaddress_std2', '')
        self.passwd = kwargs.get('userpasswd', '')
        self.pkg_root_path = kwargs.get('pkg_root_path', '')
        self.db_root_path = kwargs.get('db_root_path', '')
        self.upgrade_user_prefix = 'upgrade_'
        self.befor_version = kwargs.get('befor_version', '')
        self.after_version = kwargs.get('after_version', '')
        self.ftp_vmap_path = kwargs.get('ftp_vmap_path')
        self.DATE = self.sh('date "+%m%d"')
        self.username = ''
        self.env_path = ''
        self.dn1_path = ''
        self.dbport = ''
        self.pkg_path = ''
        self.up_pkg_path = ''
        self.db_name = 'postgres'

    def init_base_data(self):
        self.username = self.sh(f'ls /home|grep {self.upgrade_user_prefix}')
        self.env_path = f'/home/{self.username}/gaussdb.bashrc'
        self.dn1_path = f'{self.db_root_path}/{self.username}/cluster/dn1'
        cmd = f"cat {self.dn1_path}/postgresql.conf|" \
              f"grep -w 'port ='|awk '{{print $3}}'"
        self.dbport = self.sh(cmd)
        self.pkg_path = f'{self.pkg_root_path}/{self.username}/{self.DATE}/pkg'
        self.up_pkg_path = f'{self.pkg_root_path}/{self.username}/' \
                           f'{self.DATE}/upgrade_pkg'

    def check_befor_upgrade(self, cm_flag=False):
        self.log.info('升级前准备与检查')
        self.log.info('1.获取升级前版本')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_ssh -c "gsql -V"\nEOF'
        res = self.sh(cmd)
        assert res.count(self.befor_version) == 3

        self.log.info('2.健康检查：使用gs_checkos工具完成操作系统状态检查')
        cmd = f'source {self.env_path};gs_checkos -i A'
        res = self.sh(cmd)
        assert 'Abnormal numbers:0' in res

        self.log.info('3.检查数据库节点磁盘使用率')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_ssh -c "df -h {self.db_root_path}|grep -v Filesystem|' \
              f'awk \'{{{{print \\\\\\$5}}}}\'"\nEOF'
        res = self.sh(cmd)
        regex_res = re.split('[:%]', res)
        used_list = [regex_res[2].strip(), regex_res[4].strip(),
                     regex_res[6].strip()]
        for i in used_list:
            self.log.info(i)
            assert int(i) <= 70

        self.log.info('4.检查数据库状态')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_om -t status --all\nEOF'
        res = self.sh(cmd)
        node_num = res.count('HA_state')
        if cm_flag:
            expect = r'\nstate.*:.*Normal'
        else:
            expect = r'instance_state.*:.*Normal'
        regex_res = re.findall(expect, res)
        assert len(regex_res) == node_num

        self.log.info('5.检查参数enable_stream_replication值，该参数为off时不允许升级')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_guc check -N all -I all -c "enable_stream_replication"\nEOF'
        res = self.sh(cmd)
        flag = 'enable_stream_replication=on' in res
        flag1 = 'The value of parameter enable_stream_replication ' \
                'is same on all instances'
        if not (flag and flag1):
            cmd = f'su - {self.username} <<EOF\n' \
                  f'source {self.env_path} && ' \
                  f'gs_guc set -N all -I all ' \
                  f'-c "enable_stream_replication=on" && ' \
                  f'gs_om -t restart\nEOF'
            res = self.sh(cmd)
            assert 'Failed instances: 0' in res

    def do_upgrade(self, ftp_path, arg, cm_flag=False):
        """
        执行升级
        :param ftp_path: 取包地址
        :param arg: 位置参数
        :param cm_flag: 是否带cm
        :return: 执行完返回True
        """
        self.log.info('升级操作')
        self.log.info('1.创建新包目录')
        cmd = f'mkdir -p {self.up_pkg_path}'
        res = self.sh(cmd)
        assert len(res) == 0

        self.log.info('2.将需要更新的新包上传至新包目录并解压')
        wget_cmd = f'wget {ftp_path}'

        cmd = f'cd {self.up_pkg_path} && rm -rf * && ' \
              f'{wget_cmd} && ' \
              f'tar -zxf {os.path.basename(ftp_path)} && ' \
              f'for i in `ls|grep \'tar.gz\'|grep -v Lite|grep -v Libpq`; ' \
              f'do tar -zxf "$i"; ' \
              f'done'
        res = self.sh(cmd, is_print=False)
        assert '100%' in res

        res = self.sh(f'ls {self.up_pkg_path}|wc -l')
        assert int(res) > 1

        self.log.info('3.进入安装包解压出的script目录下,在就地升级或灰度升级前执行前置脚本gs_preinstall')
        shell_cmd = f'''cd {os.path.join(self.up_pkg_path, 'script')} &&
            expect <<EOF
            set timeout 600
            spawn ./gs_preinstall -U {self.username} -G {self.username} \
            -X {self.pkg_path}/{self.username}.xml \
            --sep-env-file={self.env_path}
            expect {{
                "*(yes/no)?" {{ send "yes\\r";exp_continue }}
                "*assword:" {{ send "{self.passwd}\\r" }}
            }}
            expect {{
                "*(yes/no)?" {{ send "yes\\r";exp_continue }}
                "*assword:" {{ send "{self.passwd}\\r";exp_continue }}
                "*assword:" {{ send "{self.passwd}\\r" }}
                eof
            }}''' + '\nEOF\n'
        res = self.sh(shell_cmd)
        assert 'Preinstallation succeeded' in res

        self.log.info('4.切换至omm用户, gs_om -V查看版本')
        cmd = f'su - {self.username} << EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_ssh -c "gs_om -V"\nEOF\n'
        res = self.sh(cmd)
        assert res.count(f'openGauss OM {self.after_version}') == 3

        self.log.info('5.数据库状态正常时，开始升级')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_om -t status --all\nEOF'
        res = self.sh(cmd)
        if cm_flag:
            expect = r'\nstate.*:.*Normal'
        else:
            expect = r'instance_state.*:.*Normal'
        regex_res = re.findall(expect, res)
        assert len(regex_res) == 3

        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path} && ' \
              f'gs_upgradectl -t auto-upgrade ' \
              f'-X {self.pkg_path}/{self.username}.xml {arg}\nEOF'
        res = self.sh(cmd)
        assert 'commit-upgrade' in res
        return True

    def get_disconnect_time(self):
        """
        获取灰度升级过程中闪断时间
        :return: 闪断时间差
        """
        self.log.info('获取灰度升级过程中闪断时间')
        disconnect_keywords = 'failed to connect'
        break_flag = False
        circle_times = 0
        start_time = ''
        with self.ssh_connect(self.ipaddress, self.username,
                              self.passwd) as ssh:
            cmd = f"source {self.env_path}; " \
                  f"gsql -d {self.db_name} " \
                  f"-p {self.dbport} -c \"select current_database();\""
            while True:
                res = self.ssh_run(ssh, cmd, True, False)
                if disconnect_keywords in res and not break_flag:
                    start_time = datetime.datetime.now()
                    break_flag = True
                if disconnect_keywords not in res and self.db_name in res \
                        and break_flag:
                    end_time = datetime.datetime.now()
                    disconn_time = (end_time - start_time).seconds
                    self.log.info(
                        f'断连开始时间：{start_time}, 断连结束时间：{end_time}, '
                        f'断连时长：{disconn_time}')
                    break
                time.sleep(1)
                circle_times += 1
                if circle_times > 3600:
                    break
        return '监控任务执行成功'

    def after_upgrade(self, cm_flag=False):
        self.log.info('升级后验证')
        self.log.info('1.版本验证')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_ssh -c "gsql -V"\nEOF'
        res = self.sh(cmd)
        assert res.count(self.after_version) == 3

        self.log.info('2.健康检查：使用gs_checkos工具完成操作系统状态检查')
        cmd = f'source {self.env_path};gs_checkos -i A'
        checkos_res = self.sh(cmd)
        pattern = 'A6.*?:.*?Abnormal'
        regex_res = re.search(pattern, checkos_res)
        if regex_res:
            self.sh(f'source {self.env_path};gs_checkos -i B1')
            checkos_res = self.sh(cmd)
        assert 'Abnormal numbers:0' in checkos_res

        self.log.info('3.检查数据库状态')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_om -t status --all\nEOF'
        res = self.sh(cmd)
        if cm_flag:
            expect = r'\nstate.*:.*Normal'
        else:
            expect = r'instance_state.*:.*Normal'
        regex_res = re.findall(expect, res)
        self.log.info(regex_res)
        assert len(regex_res) == 3

    def commit_upgrage(self):
        self.log.info('提交升级')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_upgradectl -t commit-upgrade ' \
              f'-X {self.pkg_path}/{self.username}.xml\nEOF'
        res = self.sh(cmd)
        assert 'Commit upgrade succeeded' in res or \
               'Commit binary upgrade succeeded' in res

        self.log.info('版本验证')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_ssh -c "gaussdb -V"\nEOF'
        res = self.sh(cmd)
        assert res.count(self.after_version) == 3

    def rollback(self, arg, expect, db_version):
        self.log.info('升级版本回滚')
        cmd = f'su - {self.username} <<EOF\n' \
              f'source {self.env_path}\n' \
              f'gs_upgradectl -t auto-rollback ' \
              f'-X {self.pkg_path}/{self.username}.xml {arg}\nEOF'
        res = self.sh(cmd)
        assert expect in res

        cmd = f'su - {self.username} -c "source {self.env_path}; gaussdb -V"'
        res = self.sh(cmd)
        assert f'openGauss {db_version}' in res